package a.autocode;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import a.autocode.icop.maker.ICOPServiceMaker;
import jplat.tools.coder.JsonCoder;
import jplat.tools.config.JConfigUtils;
import jplat.tools.file.XFileTools;
import jplat.tools.string.DateUtil;
import jplat.tools.string.StringUtil;
import jplat.tools.string.XLog;

/**
 * 渠道代码自动生成工具类.
 * @author zhangcq
 * @date Dec 5, 2016
 * @comment
 */
public class JBaseServiceMaker
{
	//配置文件读取和文件字符集
	protected String confFile;
	protected String charset;

	public boolean override = false;		//是否覆盖已有文件.

	public JBaseConfigInfo config;

	public JBaseServiceMaker( String confpath, String charset_ )
	{
		//created in sub-class.
		confFile = confpath;
		charset = charset_;

		XLog.log("[     CONFIG ]:"+confpath);
	}

	public void doParseConfFile() throws IOException
	{
		//解析环境配置文件.
		parseConfFile(confFile);

		//解析接口配置文件.
		parseConfFile(config.getPortPath());
	}

	/*****
	 * 解析列表文件.
	 * @param file
	 * @throws IOException
	 */
	protected void parseConfFile( String confpath ) throws IOException
	{
		String clp = "classpath:";
		BufferedReader reader = null;
		if ( confpath.startsWith(clp) )
		{
			String cnf = confpath.substring(clp.length());
			XLog.log("__LOAD CLASSPATH config-file1:%s", cnf );
//			reader = new BufferedReader( new InputStreamReader(getClass().getClassLoader().getResourceAsStream(cnf),"utf-8"));
			reader = new BufferedReader( new InputStreamReader(JConfigUtils.class.getClassLoader().getResourceAsStream(cnf),charset));
		}
		else
		{
			reader = new BufferedReader( new InputStreamReader(new FileInputStream(new File(confpath)),charset));
		}

		String line="";
		while ( true )
		{
			line = reader.readLine();
			if ( line == null )		//解析到最后一行.
			{
				break;
			}
			
//			XLog.log("__READ_LINE:%s", line);

			line = line.trim();
			if ( StringUtil.isEmpty(line) || line.charAt(0)=='#')
			{
				continue;
			}

			//解析命令语句.
			if (line.charAt(0)=='@')
			{
				if( !doParseLine(line.substring(1)) )
				{
					log("command error.");
					return;
				}

				continue;
			}

			if ( !doParseField( line ) )
			{
				log("parse field error.");
			}
		}

		reader.close();
	}

	/**
	 * 解析一行
	 * @author zhangcq
	 * @date Dec 12, 2016
	 * @comment 
	 * @param line
	 * @return
	 */
	private boolean doParseLine( String line )
	{
		String parts[] = line.split("=");
		String name = parts[0];
		String value = "";
		if ( parts.length == 2 )
		{
			value = parts[1];
		}
		
		doParseCmd( name, value,parts);
		return true;
	}

	/**
	 * 解析命令语句.
	 * @author zhangcq
	 * @date Dec 5, 2016
	 * @comment 
	 * @return
	 */
	private boolean doParseCmd( String name, String value0, String parts[] )
	{
		String value = value0;

		//特殊处理路径.
		if ( StringUtil.isNotEmpty(value) && value.charAt(0) == '+' )
		{
			value = value.substring(1);
			if ( config.getSrcPath() != null )
			{
				value = config.getSrcPath() + value;
				/*				if ( config.getSrcPath().endsWith(File.separator+"")
						|| value.startsWith(File.separator+"") )
				{
					value = config.getSrcPath() + value;
				}
				else
				{
					value = config.getSrcPath()+File.separator+value;
				}*/
			}
		}

		//保存所有配置.
		config.putConfValue(name, value);

		//源码路径 可以用反射简化优化.
		if ( JBaseConfigInfo.CMD_SRC_PATH.equals(name))
		{
			config.setSrcPath(value);
		}
		else if ( JBaseConfigInfo.CMD_BEAN_PACKAGE.equals(name))
		{
			config.setBeanPackage(value);
		}
		else if ( JBaseConfigInfo.CMD_BEAN_TEMP_PATH.equals(name))
		{
			config.setBeanTemplatePath(value);
		}
		else if ( JBaseConfigInfo.CMD_PORT_PATH.equals(name))
		{
			config.setPortPath(value);
		}
		else if ( JBaseConfigInfo.CMD_PORT_BACKUP_PATH.equals(name))
		{
			config.setPortBackupPath(value);
		}
		else if (JBaseConfigInfo.CMD_BEAN_NAME.equals(name))
		{
			config.setBeanName(value);
		}
		else if (JBaseConfigInfo.CMD_BEAN_DESC.equals(name))
		{
			config.setBeanDesc(value);
		}
		else if (JBaseConfigInfo.CMD_CHANGE.equals(name))
		{
			config.setChange(value);;
		}
		else if (JBaseConfigInfo.CMD_REFURL.equals(name))
		{
			config.setRefUrl(value);
		}
		else if (JBaseConfigInfo.CMD_GDATE.equals(name))
		{
			config.setGdate(value);
		}
		else if (JBaseConfigInfo.CMD_REQUEST.equals(name))
		{
			config.action=JBaseConfigInfo.ACT_PARSE_REQ;
		}
		else if (JBaseConfigInfo.CMD_RESPONSE.equals(name))
		{
			config.action=JBaseConfigInfo.ACT_PARSE_RSP;
		}
		else if (JBaseConfigInfo.CMD_RESPONSE.equals(name))
		{
			config.action=JBaseConfigInfo.ACT_PARSE_RSP;
		}
		else if (JBaseConfigInfo.CMD_LIST_START.equals(name))
		{
			if ( StringUtil.isEmpty(value) )
			{
				log("error:value is empty,key="+name);
				return false;
			}

			JBaseFieldInfo fdd = new JBaseFieldInfo(value);
			fdd.setType(JBaseFieldInfo.TP_LIST);
			config.addField(fdd);

			//判断当前行为.
			if (config.action==JBaseConfigInfo.ACT_PARSE_RSP)
			{
				config.action=JBaseConfigInfo.ACT_PARSE_RSP_LIST;
			}
			else
			{
				config.action=JBaseConfigInfo.ACT_PARSE_REQ_LIST;
			}
		}
		else if (JBaseConfigInfo.CMD_LIST_END.equals(name))
		{
			if ( config.action == JBaseConfigInfo.ACT_PARSE_RSP_LIST )
			{
				config.action=JBaseConfigInfo.ACT_PARSE_RSP;
			}
			else
			{
				config.action=JBaseConfigInfo.ACT_PARSE_REQ;
			}
		}

		return parseCmd(name,value0,value);
	}

	//子类实现回调.
	protected boolean parseCmd( String name, String value0, String value1 )
	{
		return true;
	}

	/***
	 * 解析每一行数据并保存到配置里面去.
	 * @author zhangcq
	 * @date Dec 5, 2016
	 * @comment 
	 * @param line
	 * @return
	 */
	private boolean doParseField( String line )
	{
		JBaseFieldInfo field = parserLine(line);
		config.addField(field);
		return true;
	}

	/**
	 * 解析每一行接口数据.
	 * @author zhangcq
	 * @date Dec 5, 2016
	 * @comment 适用于格式:ProductId	支付产品标识
	 * @param line
	 * @return
	 */
	private JBaseFieldInfo parserLine( String line )
	{
		if ( StringUtil.isEmpty(line) )
		{
			throw new RuntimeException("empty line.");
		}

		line=line.trim();
		String parts[] = line.split("\t");
/*		if ( parts.length < 2 )
		{
			throw new RuntimeException("error format, field-name and type are required.");
		}*/
		String fieldName = parts[0];
		String type="",chName="",desc="",length = "";
		String required = "";
		
		//从1开始哦.
		for ( int i = 1; i < parts.length; ++i )
		{
			String value = parts[i].trim();

			//按照模式判断类型  N/S/LIST [ N10 N10,2 S64 LIST] 类型不输入则默认是String类型.
			if ( StringUtil.isEmpty(type) )
			{
				String ftype = ""+value.charAt(0);
				if ( JBaseFieldInfo.OK.equals(JBaseFieldInfo.typeMap.get(ftype)) )
				{
					type = ftype;
					length = value.substring(1);
					continue;
				}
				else if ( JBaseFieldInfo.OK.equals(JBaseFieldInfo.typeMap.get(value)) )
				{
					type = value;
					continue;
				}
			}
			
			//必输项目判断Y,N
			if ( StringUtil.isEmpty(required) )
			{
				if ( JBaseFieldInfo.OK.equals(JBaseFieldInfo.requiredMap.get(value)) )
				{
					required = value;
					continue;
				}
			}
			
			//中文解释.
			if ( StringUtil.isEmpty(chName) )
			{
				//解析中文名称.
				int index = value.indexOf(" ");
				if ( index != -1 )
				{
					chName = value.substring(0,index);
					desc = value.substring(index+1);
					continue;
				}
				else
				{
					chName = value;
				}
			}
		}
		
		//检查解析结果.
		JBaseFieldInfo field = new JBaseFieldInfo(fieldName);
		
		//检查类型.
		field.setType(JBaseFieldInfo.TP_STRING);
		if ( StringUtil.isNotEmpty(type) )
		{
			field.setType(type);
		}
		
		//长度
		field.setLength("");
		if ( StringUtil.isNotEmpty(length) )
		{
			field.setLength(length);
		}
		
		//检查必输项.
		field.setRequired(false);
		if ( JBaseFieldInfo.REQUIED_YES.equals(required) )
		{
			field.setRequired(true);
		}

		//中文名字和描述.
		field.setChName(chName);
		field.setDesc(desc);

		return field;
	}

	/**
	 * 遍历配置文件生成代码.
	 * @author zhangcq
	 * @date Dec 5, 2016
	 * @comment 
	 * @return
	 */
	public boolean doAutoCode()
	{
		printGenLogs();

		doOneBeanCode( 0, config.getBeanName(),
				JBaseConfigInfo.BEAN_TYPE_REQ,
				config.getBeanTypeName(JBaseConfigInfo.BEAN_TYPE_REQ),
				config.getBeanTemplatePath(),
				config.getBeanDir(),
				config.getReqList() );

		doOneBeanCode( 0, config.getBeanName(),
				JBaseConfigInfo.BEAN_TYPE_RSP,
				config.getBeanTypeName(JBaseConfigInfo.BEAN_TYPE_RSP),
				config.getBeanTemplatePath(),
				config.getBeanDir(),
				config.getRspList());

		return true;
	}

	/**
	 * 打印日志信息.
	 * @author zhangcq
	 * @date Dec 12, 2016
	 * @comment
	 */
	private void printGenLogs()
	{
		//打印输出信息.
		XLog.log("[    SRC_DIR ]:"+config.getSrcPath());
		XLog.log("[ PACKG_NAME ]:"+config.getBeanPackage());

		String desc = config.getConfValue("BEAN_DESC");
		if ( StringUtil.isEmpty(desc) )
		{
			desc = config.getConfValue("TRANS_DESC");
		}
		XLog.log("[       DESC ]:"+desc);
		XLog.log("--------------------------------------");
	}

	/***
	 * 根据配置自动生成代码.
	 * 每调用一次生成一个bean class的文件.
	 * @author zhangcq
	 * @date Dec 16, 2016
	 * @comment 
	 * @param level			调用层级数.
	 * @param beanName		名字标记(比如PCM1008等)
	 * @param type			Req/Rsp
	 * @param beanTypeName	bean名字.
	 * @param template_file	模板文件路径.
	 * @param outpath		输出文件路径.
	 * @param list			域列表.
	 * @return
	 */
	private boolean doOneBeanCode(
			int level,
			String beanName,
			String type,
			String beanTypeName,
			String template_file,
			String outpath,
			List<JBaseFieldInfo> list )
	{
		if ( list == null || list.size() == 0 )
		{
			return false;
		}

		Map<String,String> saveMap = new HashMap<String,String>();

		for ( int i = 0; i < list.size(); ++i )
		{
			JBaseFieldInfo fi = list.get(i);

			//子类回调.
			doMapSave(getTempKeyValue(fi,beanName,type),saveMap);
			if ( JBaseFieldInfo.TP_LIST.equals(fi.getType()) )
			{
				doOneBeanCode( level+1,
						beanName,
						type,
						fi.javaListBeanTypeName(beanName, type),
						template_file,
						outpath,
						fi.fieldList );
			}
		}

		try
		{
			String filepath = outpath+File.separator+beanTypeName+".java";

			//不覆盖，则先检测是否存在.
			if ( !override )
			{
				File myfile = new File(filepath);
				if ( myfile.exists() )
				{
					log(String.format("ERROR:file %s exist at path:%s", myfile.getName(), myfile.getAbsolutePath() ));
					return false;
				}
			}

			//将模板Key一个一个替换掉.
			String autCode = replaceTempl(level,template_file,beanTypeName,type,saveMap);
			OutputStream out = new FileOutputStream(filepath);
			out.write(autCode.getBytes("utf-8"));
			out.close();

			XLog.log("file-generate succeed:"+beanTypeName+".java");
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();

			return false;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return false;
		}

		return true;
	}

	/**
	 * 合并Map
	 * @author zhangcq
	 * @date Dec 12, 2016
	 * @comment 
	 * @param fromMap
	 * @param toMap
	 */
	private void doMapSave( Map<String,String> fromMap, Map<String,String> toMap )
	{
		for ( String fs : fromMap.keySet() )
		{
			String tmpValue = toMap.get(fs);
			if ( StringUtil.isNotEmpty(tmpValue) )	//追加
			{
				tmpValue += fromMap.get(fs);
				toMap.put(fs, tmpValue);
			}
			else
			{
				toMap.put(fs, fromMap.get(fs));		//新增.
			}

		}
	}

	/**
	 * 获取模板值.
	 * 生成文件时,子类可以覆盖该方法指定生成对应的值.
	 * @author zhangcq
	 * @date Dec 12, 2016
	 * @comment 
	 * @return
	 */
	protected Map<String,String> getTempKeyValue( JBaseFieldInfo field, String beanName, String type )
	{
		return null;
	}

	/**
	 * 替换模板内容.
	 * @author zhangcq
	 * @date Dec 12, 2016
	 * @comment 
	 * @return
	 */
	private String replaceTempl( int level, String tlfile, String beanTypeName, String type, Map<String,String> priMap )
	{
		//初始化模板内容.
		String template = XFileTools.loadFile(tlfile, "utf-8");
		//		log("\n__TMP:"+template+"\n");

		//初始化模板.
		config.confMap.put("GT_TIME", DateUtil.todayStr("yyyy/MM/dd/HH:mm:ss") );
		config.confMap.put("GT_DATE", DateUtil.todayStr("yyyy-MM-dd") );

		//统一配置映射.
		template = template.replaceAll(PL_BEAN_NAME, beanTypeName );	//bean名字

		//遍历 配置表
		for ( String confKey : config.confMap.keySet() )
		{
			template = template.replaceAll("__"+confKey+"__",config.confMap.get(confKey));
		}

		//遍历自定义表.
		for ( String priKey : priMap.keySet() )
		{
			template = template.replace("__"+priKey+"__",priMap.get(priKey));
		}

		//供子类特殊回调.
		template = doTmplReplace(level,template,beanTypeName,type,priMap);

		return template;
	}

	/**
	 * 在替换模板的时候供子类实现自定义回调.
	 * @author zhangcq
	 * @date Dec 12, 2016
	 * @comment 
	 * @param template
	 * @param beanTypeName
	 * @param type
	 * @param priMap
	 * @return
	 */
	protected String doTmplReplace( int level, String template, String beanTypeName, String type, Map<String,String> priMap )
	{
		return template;
	}

	public static void log( String format, Object... args )
	{
		System.out.println(String.format(format, args));
	}

	/**
	 * 备份目录文件.
	 * @author zhangcq
	 * @date Jan 13, 2017
	 * @comment 
	 * @param filepath
	 */
	protected void doPortFileBackup( String backupfilepath )
	{
		String portFile = config.getPortPath();

		try
		{
			byte[] date = String.format("#***  Created by %7.7s ***#\n@GDATE=%s\n#* ^  do not copy above ^  *#\n#***************************#\n\n", System.getProperty("user.name"),DateUtil.todayStr("yyyy/MM/dd HH:mm:ss")).getBytes();
			byte[] data = XFileTools.loadFile(portFile);

			byte[] all = new byte[date.length+data.length];
			System.arraycopy(date, 0, all, 0, date.length);
			System.arraycopy(data, 0, all, date.length, data.length);

			XFileTools.writeFile(backupfilepath, all);
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	//模板文件占位符
	protected static String PL_GTIME = "GT_TIME";
	protected static String PL_GDATE = "GT_DATE";
	protected static String PL_BEAN_NAME = "__BEAN_NAME__";			//请求返回bean名称 .
	protected static String PL_BEAN_FIELDS = "BEAN_FIELDS";			//bean成员信息.
	protected static String PL_BEAN_PROPS = "BEAN_PROPS";			//bean成员信息.


/*	public static void main(String args[])
	{
//		showCallExample();
//		test();
	}*/
	
	private static void test()
	{
		JBaseServiceMaker maker = new JBaseServiceMaker("","utf-8");
		
		String line = "loginType	登录类型 p-密码的 h-手势密码.	Y	S128";
		JBaseFieldInfo filed = maker.parserLine(line);
		XLog.log("JSONCODE:%s", JsonCoder.toJsonString(filed) );
	}
	
	private static void showCallExample()
	{
		//配置文件读取和文件字符集
		String confpath = "E:\\ebanksvn2\\source\\pro000\\directBankPro000\\dolphinq\\src\\a\\autocode\\example_interface.conf";

		try
		{
			JBaseServiceMaker maker = new JBaseServiceMaker(confpath,"utf-8");
			maker.override = false;
			maker.doParseConfFile();
			maker.doAutoCode();

			//生成service文件.
			//			XLog.log(JsonCoder.toJsonString(maker.config));
		}
		catch (IOException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
